#ifndef _KLAS_Vector2_HPP_
#define _KLAS_Vector2_HPP_

#include "../include/Defines.hpp"
#include <cmath>
#include <cassert>
#include <limits>

namespace klas
{	
	//! The Vector2<T> can represent, nearly, any 2D object.
	template<class T = Real>
	struct Vector2
	{
		//! X component
		T x, 
	
		//! Y component
			y;

		//! Ctor
		Vector2()
		{
			x = 0.0, y = 0.0;
		}
	
		//! Ctor
		Vector2(T x, T y)
		{
			this->x = x, this->y = y;
		}
		
		//! Sets x and y to zero.
		void zero()
		{
			x = 0.0, y = 0.0;
		}
		
		//! Returns if the vector is zero.
		/*! \return True if the vector is zero. */
		bool isZero()
		{
			return ((fabs(x) < 1E-12) && (fabs(y) < 1E-12));
		}
  
    //! Returns this vector's unit vector (vector of magnitude 1 in the same dir)
    Vector2<T> normalize()
    {
		  Real inverseMagnitude = 1.0/getLength();
		  return Vector2(x*inverseMagnitude, y*inverseMagnitude);
		}

		//! Normalizes the vector
		/*void normalize()
		{
			T vector_length = this->getLength();

			if (vector_length > std::numeric_limits<T>::epsilon())
			{
				this->x /= vector_length;
				this->y /= vector_length;
			}
		}*/

		//! Gets the vector perpendicular to this one.
		/*! \return The vector perpendicular to this one. */
		Vector2<T> getPerp()
		{
			return Vector2<T>(-y, x);
		} 
		
		//! Gets the length / magnitude of the vector.
		/*! \return The vector's length. */
		T getLength() const
		{
			return sqrt(x * x + y * y);
		}
	
		//! Gets the length squared of the vector
		/*! \return The vector's length squared. */
		T getLengthSq() const
		{
			return (x * x) + (y * y);
		}
	
		//! Gets the dot product of the vector.
		/*! \param other The vector to compute the dot product against.
				\return The dot product between the two vectors. */
		T getDot(const Vector2<T>& other) const
		{
			return ((x * other.x) + (y * other.y));
		}
		
		//! Gets the distance between this vector and the other one.
		/*! \param other The vector to compute the distance against.
				\return The distance between the two vectors. */
		T getDistance(const Vector2<T>& other) const
		{
			T xSeparation = other.x - x,
					 ySeparation = other.y - y;
				   
			return sqrt((xSeparation * xSeparation) + (ySeparation * ySeparation));
		}
		
		//! Gets the reverse of this vector.
		/*! \return The reverse of this vector. */
		Vector2<T> getReverse() const
		{
			return Vector2<T>(-x, -y);
		}
		
		//! Gets the cross product between this vector and the other one.
		/*! \param other The vector to compute the cross product against.
				\return The cross product between the two vectors. */
		T getCross(const Vector2<T>& other) const
		{
			return (x * other.y) - (y * other.x); 
		}
		
		//! Truncates the vector
		/*! \param max The maximum value of the vector. */
		void truncate(T max)
		{
			if (this->getLength() > max)
			{
				this->normalize();

				this->x *= max;
				this->y *= max;
			}
		}
		
		// Operator overloads
		bool operator ==(const Vector2<T>& rhs)
		{
			return ((fabs(x-rhs.x) < 1E-12) && (fabs(y-rhs.y) < 1E-12));
		}
		
		bool operator !=(const Vector2<T>& rhs)
		{
			return !((fabs(x-rhs.x) < 1E-12) && (fabs(y-rhs.y) < 1E-12));
		}



		friend Vector2<T> operator-(const Vector2<T>& a)
		{
			return Vector2<T>(-a.x, -a.y);
		}
		
		friend Vector2<T> operator+(const Vector2<T>& a, const Vector2<T>& b)
		{
			return Vector2<T>(a.x+b.x, a.y+b.y);
		}
		
		friend Vector2<T> operator-(const Vector2<T>& a, const Vector2<T>& b)
		{
			return Vector2<T>(a.x-b.x, a.y-b.y);
		}
		
		friend Vector2<T> operator*(const Vector2<T>& a, const Vector2<T>& b)
		{
			return Vector2<T>(a.x * b.x, a.y *b.y);
		}
		
		friend Vector2<T> operator/(const Vector2<T>& a, const Vector2<T>& b)
		{
			return Vector2<T>(a.x/b.x, a.y/b.y);
		}
		
		friend Vector2<T> operator+=(Vector2<T>& a, const Vector2<T>& b)
		{
			a.x += b.x;
			a.y += b.y;
			return a;
		}
		
		friend Vector2<T> operator-=(Vector2<T>& a, const Vector2<T>& b)
		{
			a.x -= b.x;
			a.y -= b.y;
			return a;
		}
		
		friend Vector2<T> operator*=(Vector2<T>& a, const Vector2<T>& b)
		{
			const T cy = a.y * b.y;
			const T cx = a.x * b.x;
			a.x = cx;
			a.y = cy;
			return a;
		}
		
		
		friend Vector2<T> operator*(const Vector2<T>& a, T s)
		{
			return Vector2<T>(a.x*s, a.y*s);
		}
		
		friend Vector2<T> operator/(const Vector2<T>& a, T s)
		{
			assert(s != 0 && "Vector2<T> attempted to divide by 0");
			return Vector2<T>(a.x/s, a.y/s);
		}
		
		friend Vector2<T> operator*=(Vector2<T>& a, T s)
		{
			a.x *= s;
			a.y *= s;
			return a;
		}
		
		friend Vector2<T> operator/=(Vector2<T>& a, T s)
		{
			assert(s!=0 && "Vector2<T> attempted to divide by 0");
			a.x /= s;
			a.y /= s;
			return a;
		}
		
		friend Vector2<T> operator*(T s, const Vector2<T>& a)
		{
			return Vector2<T>(a.x*s, a.y*s);
		}
		
		friend Vector2<T> operator*=(T s, Vector2<T>& a)
		{
			a.x *= s;
			a.y *= s;
			return a;
		}
		

	};

}
#endif

